Skip to content

07 类与面向对象

面向对象编程用类来组织数据和行为。Python的类机制简洁灵活,支持继承、多态、私有变量,还有迭代器和生成器这两个强大特性。

一、定义类

1.1 基本语法

python
>>> class MyClass:
...     """一个简单的类"""
...     i = 12345       # 类变量
...
...     def f(self):    # 方法
...         return 'hello world'
...
>>> x = MyClass()
>>> x.f()
'hello world'

self是实例本身,调用方法时不需要传。

1.2 __init__构造方法

python
>>> class Complex:
...     def __init__(self, realpart, imagpart):
...         self.r = realpart   # 实例变量
...         self.i = imagpart
...
>>> x = Complex(3.0, -4.5)
>>> x.r
3.0
>>> x.i
-4.5

1.3 实例变量和类变量

python
>>> class Dog:
...     kind = 'canine'         # 类变量,所有实例共享
...
...     def __init__(self, name):
...         self.name = name    # 实例变量,每个实例独立
...
>>> d = Dog('Fido')
>>> e = Dog('Buddy')
>>> d.kind
'canine'
>>> d.name
'Fido'
>>> d.kind is e.kind
True

类变量是共享的,实例变量是独立的。可变对象(列表、字典)做类变量要小心:

python
>>> class Dog:
...     tricks = []             # 错误!所有实例共享同一个列表
...
...     def __init__(self, name):
...         self.name = name
...
...     def add_trick(self, trick):
...         self.tricks.append(trick)

应该在__init__里初始化:

python
>>> class Dog:
...     def __init__(self, name):
...         self.name = name
...         self.tricks = []    # 每个实例独立的列表
...
...     def add_trick(self, trick):
...         self.tricks.append(trick)

二、继承

2.1 基本继承

python
>>> class Animal:
...     def speak(self):
...         return "..."
...
... class Dog(Animal):
...     def speak(self):
...         return "汪汪"
...
>>> d = Dog()
>>> d.speak()
'汪汪'

2.2 isinstance()和issubclass()

python
>>> isinstance(d, Dog)
True
>>> isinstance(d, Animal)
True
>>> issubclass(Dog, Animal)
True

2.3 多重继承

python
>>> class A:
...     def hello(self):
...         print("A")
...
>>> class B(A):
...     def hello(self):
...         print("B")
...
>>> class C(A):
...     def hello(self):
...         print("C")
...
>>> class D(B, C):
...     pass
...
>>> d = D()
>>> d.hello()
B

Python用C3线性化算法解析多重继承的顺序,可以用D.__mro__查看方法解析顺序。

三、私有变量

3.1 约定和名称改写

python
>>> class Mapping:
...     def __init__(self, iterable):
...         self.items_list = []
...         self.__update(iterable)
...
...     def update(self, iterable):
...         for item in iterable:
...             self.items_list.append(item)
...
...     __update = update   # 私有副本
...
>>> class MappingSubclass(Mapping):
...     def update(self, keys, values):
...         # 不会覆盖__update
...         for item in zip(keys, values):
...             self.items_list.append(item)

__开头的名称会被改写为_类名__名称,避免和子类冲突。这不是真正的私有,只是名称改写。

3.2 约定

  • _开头:受保护的,外部可以访问但不推荐
  • __开头:私有的,名称会被改写
  • 没有前缀:公开的

四、迭代器

4.1 迭代协议

python
>>> class Reverse:
...     def __init__(self, data):
...         self.data = data
...         self.index = len(data)
...
...     def __iter__(self):
...         return self
...
...     def __next__(self):
...         if self.index == 0:
...             raise StopIteration
...         self.index -= 1
...         return self.data[self.index]
...
>>> for char in Reverse('spam'):
...     print(char, end=' ')
...
m a p s

实现了__iter__()__next__()的对象就是迭代器。for循环自动调用这两个方法。

4.2 StopIteration

__next__()在没有更多元素时抛出StopIterationfor循环捕获它并结束循环。

五、生成器

5.1 基本用法

生成器是创建迭代器的简单方式:

python
>>> def reverse(data):
...     for index in range(len(data) - 1, -1, -1):
...         yield data[index]
...
>>> for char in reverse('spam'):
...     print(char, end=' ')
...
m a p s

yield让函数变成生成器函数,每次调用next()时执行到下一个yield

5.2 生成器表达式

python
>>> sum(i*i for i in range(10))         # 求平方和
285

>>> list(i*i for i in range(10))        # 转成列表
[0, 1, 4, 9, 16, 25, 36, 49, 64, 81]

>>> unique_words = set(word for line in page for word in line.split())

生成器表达式用圆括号,比列表推导式更省内存(按需生成,不一次性创建所有元素)。

六、魔术方法

6.1 常用魔术方法

python
>>> class Vector:
...     def __init__(self, x, y):
...         self.x = x
...         self.y = y
...
...     def __repr__(self):
...         return f'Vector({self.x!r}, {self.y!r})'
...
...     def __abs__(self):
...         return (self.x ** 2 + self.y ** 2) ** 0.5
...
...     def __add__(self, other):
...         return Vector(self.x + other.x, self.y + other.y)
...
...     def __mul__(self, scalar):
...         return Vector(self.x * scalar, self.y * scalar)
...
...     def __bool__(self):
...         return bool(abs(self))
...
>>> v = Vector(3, 4)
>>> v
Vector(3, 4)
>>> abs(v)
5.0
>>> v + Vector(1, 2)
Vector(4, 6)
>>> v * 3
Vector(9, 12)

6.2 常见魔术方法列表

方法说明
__init__构造方法
__repr__字符串表示(给程序员看)
__str__字符串表示(给用户看)
__len__len()
__getitem__索引访问
__setitem__索引赋值
__contains__in运算符
__add__+
__mul__*
__eq__==
__lt__<
__bool__布尔值
__iter__迭代器
__next__获取下一个元素
__enter__/__exit__with语句
__call__函数调用

七、总结

概念说明
类变量所有实例共享
实例变量每个实例独立
继承子类继承父类
多重继承C3线性化解析顺序
__私有名称改写,不是真私有
迭代器__iter__() + __next__()
生成器yield关键字
生成器表达式圆括号,按需生成

类变量用在不可变类型上,可变类型要在__init__里初始化。生成器比列表推导式省内存,适合大数据量场景。